﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public interface IFakeable
{
    int Id { get; set; }
    void CreateFakeData(int id);
}

public class FakeRepository<T> where T:IFakeable
{
    private bool _useWebPersistence = false;
    protected List<T> _list = new List<T>();
    private readonly string CACHE_KEY;

    protected virtual List<T> List
    {
        get
        {
            if (this._useWebPersistence)
            {
                if (HttpContext.Current.Session[CACHE_KEY] != null)
                {
                    // find entry by type name
                    this._list = HttpContext.Current.Session[CACHE_KEY] as List<T>;
                }
                else
                {
                    HttpContext.Current.Session.Add(CACHE_KEY, this._list);
                }
            }
            return this._list;
        }
    }

    public virtual bool HasItems
    {
        get { return this.List.Count > 0; }
    }

    public FakeRepository()
    {
        this._useWebPersistence = (HttpContext.Current != null);
        this.CACHE_KEY = this._list.GetType().ToString();
    }

    public static FakeRepository<T> Create()
    {
        return new FakeRepository<T>();
    }

    protected virtual void Persist()
    {
        if (this._useWebPersistence)
        {
            HttpContext.Current.Session[CACHE_KEY] = this.List;
        }
    }

    public virtual T GetById(int id)
    {
        return this.List.SingleOrDefault<T>(b => b.Id == id);
    }

    public virtual bool Exists(T item)
    {
        var itm = this.List.SingleOrDefault<T>(b => b.Id == item.Id);

        bool exists = false;
        if (itm != null)
        {
            if (item.Id > 0)
            {
                exists = true;
            }
        }
        return exists;
    }

    public virtual void Persist(T item)
    {
        if (this.Exists(item))
        {
            this.Update(item);
        }
        else
        {
            this.Insert(item);
        }
    }

    public virtual void Insert(T item)
    {
        item.Id = this.GetNextId();
        this.List.Add(item);
        this.Persist();
    }

    public virtual void Update(T item)
    {
        var oldItem = this.GetById(item.Id);
        this.List.Remove(oldItem);
        this.List.Add(item);
        this.Persist();
    }

    public virtual void Delete(int Id)
    {
        var itm = this.GetById(Id);
        this.List.Remove(itm);
        this.Persist();
    }

    public virtual IList<T> GetAll()
    {
        return (from b in this.List orderby  b.Id descending select b).ToList<T>();
    }

    public virtual FakeRepository<T> Add(int numberOfItems)
    {
        return this.Add(numberOfItems, true);
    }

    public virtual FakeRepository<T> Add(int numberOfItems, bool addOnlyIfEmpty)
    {
        var items = (from b in this.List select b).Take(numberOfItems);
        int foundCount = items.Count();

        bool add = (addOnlyIfEmpty && (foundCount == 0));

        if (add)
        {
            if (foundCount < numberOfItems)
            {
                // find difference between what we have and what we need...
                int limit = (numberOfItems - foundCount);

                int offset = 0;
                if (foundCount > 0)
                {
                    offset = this.GetNextId();
                }

                for (int i = offset; i < (limit + offset); i++)
                {
                    IFakeable item = Activator.CreateInstance<T>() as IFakeable;
                    item.CreateFakeData(i);
                    this.List.Add((T)item);
                }

                this.Persist();
            }
        }
        return this;
    }

    protected virtual int GetNextId()
    {
        if (this.List.Count > 0)
        {
            return (from b in this.List where b.Id > -1 select b.Id).Max() + 1;    
        }
        else
        {
            return 1;
        }
    }
}